home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / gs24src.zip / TRACE.C < prev    next >
C/C++ Source or Header  |  1992-02-16  |  15KB  |  516 lines

  1. /* Copyright (C) 1989, 1990 Aladdin Enterprises.  All rights reserved.
  2.    Distributed by Free Software Foundation, Inc.
  3.  
  4. This file is part of Ghostscript.
  5.  
  6. Ghostscript is distributed in the hope that it will be useful, but
  7. WITHOUT ANY WARRANTY.  No author or distributor accepts responsibility
  8. to anyone for the consequences of using it or for whether it serves any
  9. particular purpose or works at all, unless he says so in writing.  Refer
  10. to the Ghostscript General Public License for full details.
  11.  
  12. Everyone is granted permission to copy, modify and redistribute
  13. Ghostscript, but only under the conditions described in the Ghostscript
  14. General Public License.  A copy of this license is supposed to have been
  15. given to you along with Ghostscript so you can know your rights and
  16. responsibilities.  It should be in a file named COPYING.  Among other
  17. things, the copyright notice and this notice must be preserved on all
  18. copies.  */
  19.  
  20. /* trace.c */
  21. /* Tracing package for Turbo C */
  22. #include <stdio.h>
  23. #include <ctype.h>
  24. #include <stdlib.h>
  25. #include <stdarg.h>
  26. #include <dos.h>
  27. #include <setjmp.h>
  28. #include "memory_.h"
  29. typedef unsigned char byte;
  30. #ifndef __TURBOC__            /* ****** HACK ****** */
  31. #  define _ds
  32. #  define _ss
  33. extern unsigned _stklen;
  34. #  define jb_bp(jbuf) (unsigned long)buf[0]
  35. #else                    /* ****** HACK ****** */
  36. #  define jb_bp(jbuf) buf[0].j_bp
  37. #endif
  38.  
  39.  
  40. /*
  41.  * NOTE: this package is extremely specialized.  It works only
  42.  * with the Borland C and C++ compilers, with large code models,
  43.  * on 80x86 and 80x88 processors and compatibles with x>0.
  44.  * Use at your own risk.
  45.  */
  46.  
  47. /*
  48.  * To trace a procedure, call
  49.  *    trace(proc, "procname", NULL, retsize);
  50.  * or
  51.  *    trace_name("_PROCNAME", mapfile, NULL, retsize);
  52.  * where retsize is sizeof the return value type.
  53.  * To print arguments formatted (with vprintf), use
  54.  *    trace(proc, "procname", "argformat", retsize);
  55.  * or
  56.  *    trace_name("_PROCNAME", mapfile, "argformat", retsize);
  57.  *
  58.  * If trace_flush_flag is true, for crash-prone programs,
  59.  * the trace output is flushed after every call or return.
  60.  *
  61.  * Trace output goes to the file trace_output_file, or to stdout
  62.  * if trace_output_file is not initialized.
  63.  */
  64.  
  65. /*
  66.  * To map through a symbol (.MAP) file, open the file with
  67.  *    mapfile = trace_open_map(filename, &reloc);
  68.  * which returns NULL if the map file isn't available.
  69.  * reloc is set to the relocation offset (i.e., the difference between
  70.  * actual procedure addresses and the values in the map file);
  71.  * if &reloc = NULL, nothing is stored.  Then call
  72.  *    sym = trace_next_symbol(&segaddr, mapfile);
  73.  * until it returns NULL.
  74.  * To look up a symbol in the file, call
  75.  *    segaddr = trace_find_symbol(name, mapfile);
  76.  */
  77.  
  78. #ifdef TRACEDEBUG
  79.  
  80. /* Main program for testing */
  81. main()
  82.    {    int fib(int);
  83.     char *reloc;
  84.     FILE *trace_open_map();
  85.     FILE *mapf = trace_open_map("trace.map", NULL);
  86.     char *pfib;
  87.     char *trace_find_symbol();
  88.     pfib = trace_find_symbol("_FIB", mapf);
  89.     printf("fib at %lx\n", pfib);
  90.     trace_name("_FIB", mapf, "(%d)", sizeof(int));
  91.     fib(5);
  92.    }
  93. int
  94. fib(n)
  95.     int n;
  96.    {    if ( n <= 2 ) return 1;
  97.     return fib(n-1) + fib(n-2);
  98.    }
  99. /* End of main program */
  100.  
  101. #endif
  102.  
  103. /* ------ Symbol file mapping ------ */
  104.  
  105. #define max_sym 40
  106. static char sym[max_sym+1];
  107.  
  108. FILE *
  109. trace_open_map(mapname, preloc)
  110.     char *mapname;
  111.     long *preloc;
  112. {    FILE *mapf = fopen(mapname, "rt");
  113.     char *trace_find_symbol(char *, FILE *);
  114.     if ( mapf == NULL ) return NULL;    /* can't open */
  115.     if ( preloc != NULL )
  116.        {    extern main();
  117.         char *main_addr = trace_find_symbol("_MAIN", mapf);
  118.         if ( main_addr == NULL )
  119.            {    fclose(mapf);        /* can't find _main */
  120.             return NULL;
  121.            }
  122.         *preloc = (long)main - (long)main_addr;
  123.        }
  124.     return mapf;
  125. }
  126.  
  127. char *
  128. trace_next_symbol(paddr, mapf)
  129.     char **paddr;
  130.     FILE *mapf;
  131. {    /* A symbol definition line looks like this: */
  132.     /*  ssss:oooo       namestring */
  133.     /* with exactly 7 spaces between the address and the name. */
  134.     while ( 1 )
  135.        {    char ch;
  136.         unsigned parm[2];
  137.         static char term[2] = ": ";
  138.         int pi, i;
  139.         if ( (ch = getc(mapf)) != ' ' ) goto skip;
  140.         for ( pi = 0; pi < 2; pi++ )
  141.            {    unsigned p = 0;
  142.             for ( i = 0; i < 4; i++ )
  143.                {    ch = getc(mapf);
  144.                 if ( !isxdigit(ch) ) goto skip;
  145.                 p = (p << 4) + (isdigit(ch) ? ch - '0' :
  146.                     (ch - 'A' + 10) & 0xf);
  147.                }
  148.             if ( (ch = getc(mapf)) != term[pi] ) goto skip;
  149.             parm[pi] = p;
  150.            }
  151.         for ( i = 1; i < 7; i++ )
  152.             if ( (ch = getc(mapf)) != ' ' ) goto skip;
  153.         i = 0;
  154.         while ( (ch = getc(mapf)) != '\n' )
  155.            {    if ( i == max_sym ) goto skip;    /* name too long */
  156.             sym[i++] = ch;
  157.            }
  158.         sym[i] = 0;
  159.         /* Success.  Adjust the segment and return. */
  160.         *paddr = MK_FP(parm[0] + FP_SEG(abort), parm[1]);
  161.         return sym;
  162. skip:        /* Syntax didn't match, skip the rest of the line. */
  163.         while ( ch != '\n' )
  164.            {    if ( ch == (char)EOF && feof(mapf) ) return NULL;
  165.             ch = getc(mapf);
  166.            }
  167.        }
  168. }
  169.  
  170. /* Look up a symbol in a file */
  171. char *
  172. trace_find_symbol(name, mapf)
  173.     char *name;
  174.     FILE *mapf;
  175. {    char *s;
  176.     char *r;
  177.     rewind(mapf);
  178.     while ( (s = trace_next_symbol(&r, mapf)) != NULL )
  179.         if ( !strcmp(s, name) ) return r;
  180.     return (char *)NULL;
  181. }
  182.  
  183. /* ------ Instruction and register set ------ */
  184.  
  185. /* Opcodes */
  186. #define op_ADD_IMM 0x81
  187. #define op_ADD_IMM8 0x83
  188. #define op_ADD_IMMX_X 0
  189. #define op_CALL_FAR 0x9a
  190. #define op_CMP_MR 0x39
  191. #define op_CMP_RM 0x3b
  192. #define op_ENTER 0xc8
  193. #define op_INC_REG 0x40
  194. #define op_JMP_FAR 0xea
  195. #define op_JMP_FAR_IND 0xff
  196. #define op_JMP_FAR_IND_X 0x28
  197. #define op_MOV_MR 0x8b
  198. #define op_MOV_RM 0x89
  199. #define op_NOP 0x90
  200. #define op_POP_REG 0x58
  201. #define op_PUSH_IMM 0x68
  202. #define op_PUSH_REG 0x50
  203. #define op_RETF 0xcb
  204. #define op_SUB_IMM 0x81
  205. #define op_SUB_IMM8 0x83
  206. #define op_SUB_IMMX_X 0x28
  207. /* Register numbers */
  208. #define r_AX 0
  209. #define r_CX 1
  210. #define r_DX 2
  211. #define r_BX 3
  212. #define r_SP 4
  213. #define r_BP 5
  214. #define r_SI 6
  215. #define r_DI 7
  216.  
  217. /*
  218.  * The standard entry sequence generated by Turbo C consists of:
  219.  *    If there are no arguments and no local variables, nothing;
  220.  *      if there are args but no local variables, PUSH BP, MOV BP,SP;
  221.  *      if there are local variables, ENTER size,0, or
  222.  *        PUSH BP, MOV BP,SP, SUB SP,<size>
  223.  *    Optionally, PUSH SI;
  224.  *      if the PUSH SI is present, optionally PUSH DI.
  225.  *    If the frame is larger than a certain size (0x80?),
  226.  *      CMP BP,SP; JB .+8.
  227.  *    CMP [_STKLEN],SP; JA <ok>.
  228.  */
  229.  
  230. /* ------ Stack parsing ------ */
  231.  
  232. typedef byte *i_ptr;
  233.  
  234. /* Record for BP and return */
  235. typedef struct bp_ret_s {
  236.     unsigned _ss *bp;
  237.     i_ptr ret;
  238. } bp_ret;
  239.  
  240. /* Forward declarations */
  241. void fprint_block(int *, int, FILE *);
  242.  
  243. /* ------ Dynamic tracing ------ */
  244.  
  245. /* Flush output flag */
  246. int trace_flush_flag = 0;
  247.  
  248. /* The file to use for tracing output */
  249. FILE *trace_output_file = NULL;
  250.  
  251. /* The code sequence that replaces the standard procedure entry: */
  252. typedef struct trace_entry_code_s {
  253.     byte JMP_FAR;
  254.     i_ptr entry;
  255. } trace_entry_code;
  256. static trace_entry_code trace_entry_template = {
  257.     op_JMP_FAR
  258. };
  259.  
  260. /* The entry code in the trace record */
  261. typedef struct entry_code_s {
  262.     byte PUSH_BP;
  263.     byte MOV_BP_SP, mov_X;
  264.     byte PUSH_BP2;
  265.     byte PUSH_seg; unsigned short rec_seg;
  266.     byte PUSH_off; unsigned short rec_off;    /*struct trace_rec_s near **/
  267.     byte CALL; i_ptr print_args;
  268.     /* print_args moves the old return and BP above the args, */
  269.     /* and returns a new fake BP as the value in AX. */
  270.     byte MOV_BP_AX, mov_X2;
  271.     byte INC_SP, INC_SP2;        /* pop 1 excess word */
  272.     /* The largest possible finishing code is: */
  273.     /* PUSH BP, MOV SP,BP, SUB SP,size, PUSH SI, PUSH DI, */
  274.     /* CMP [_STKLEN],SP, JMP FAR real_entry */
  275.     byte finish[18];
  276. } entry_code;
  277. static entry_code entry_template = {
  278.     op_PUSH_REG+r_BP,
  279.     op_MOV_MR, 0xc0+(r_BP<<3)+r_SP,
  280.     op_PUSH_REG+r_BP,
  281.     op_PUSH_IMM, 0,
  282.     op_PUSH_IMM, 0,
  283.     op_CALL_FAR, 0L,
  284.     op_MOV_MR, 0xc0+(r_BP<<3)+r_AX,
  285.     op_INC_REG+r_SP, op_INC_REG+r_SP
  286. };
  287. /* The exit code in the trace record */
  288. typedef struct exit_code_s {
  289.     byte PUSH_BP;
  290.     byte MOV_BP_SP, mov_X;
  291.     byte PUSH_BP2;
  292.     byte PUSH_DX;
  293.     byte PUSH_AX;
  294.     byte PUSH_seg; unsigned short rec_seg;
  295.     byte PUSH_off; unsigned short rec_off;    /*struct trace_rec_s near **/
  296.     byte CALL; i_ptr print_result;
  297.     /* print_result shuffles things back again */
  298.     byte ADD_SP, add_X, c6w;
  299.     byte MOV_BP_SP2, mov_X2;
  300.     byte POP_BP;
  301.     byte RETF;
  302. } exit_code;
  303. static exit_code exit_template = {
  304.     op_PUSH_REG+r_BP,
  305.     op_MOV_MR, 0xc0+(r_BP<<3)+r_SP,
  306.     op_PUSH_REG+r_BP,
  307.     op_PUSH_REG+r_DX,
  308.     op_PUSH_REG+r_AX,
  309.     op_PUSH_IMM, 0,
  310.     op_PUSH_IMM, 0,
  311.     op_CALL_FAR, 0L,
  312.     op_ADD_IMM8, op_ADD_IMMX_X+0xc0+r_SP, 12,
  313.     op_MOV_MR, 0xc0+(r_BP<<3)+r_SP,
  314.     op_POP_REG+r_BP,
  315.     op_RETF
  316. };
  317.  
  318. /* Trace information record */
  319. typedef struct trace_rec_s trace_rec;
  320. struct trace_rec_s {
  321.     entry_code entry_code;
  322.     exit_code exit_code;
  323.     trace_rec *next;
  324.     char *name;
  325.     char *arg_format;
  326.     int retsize;
  327.     long count;
  328. };
  329.  
  330. static trace_rec *trace_list = 0;
  331.  
  332. /* Trace a named procedure */
  333. int
  334. trace_name(name, mapf, arg_format, retsize)
  335.     char *name;
  336.     FILE *mapf;
  337.     char *arg_format;
  338.     int retsize;
  339. {    char *proc = trace_find_symbol(name, mapf);
  340.     if ( proc == (char *)NULL ) return -1;    /* name not found */
  341.     return trace((void (*)())proc, name, arg_format, retsize);
  342. }
  343.  
  344. /* Trace a procedure */
  345. int
  346. trace(proc, name, arg_format, retsize)
  347.     void (*proc)();
  348.     char *name;
  349.     char *arg_format;
  350.     int retsize;
  351. {    i_ptr pcode = (i_ptr)proc;
  352.     i_ptr pc = pcode;
  353.     i_ptr pt;
  354.     int len;
  355.     trace_entry_code *ptrace = (trace_entry_code *)pcode;
  356.     bp_ret *trace_print_arguments();
  357.     long trace_print_result();
  358.     trace_rec *rec = (trace_rec *)malloc(sizeof(trace_rec));
  359.     if ( !rec ) return -1;
  360.     rec->entry_code = entry_template;
  361.     rec->exit_code = exit_template;
  362.     /* Compare the procedure's entry sequence against the */
  363.     /* standard one.  If they differ, we can't trace the procedure. */
  364.     pt = (byte *)&rec->entry_code.finish;
  365.     if ( *pc == op_ENTER )
  366.         pc += 4;
  367.     else if ( *pc == op_PUSH_REG+r_BP && pc[1] == op_MOV_MR &&
  368.             pc[2] == 0xc0+(r_BP<<3)+r_SP )
  369.        {    pc += 3;
  370.         if ( *pc == op_SUB_IMM && pc[1] == op_SUB_IMMX_X+0xc0+r_SP )
  371.             pc += 4;
  372.         else if ( *pc == op_SUB_IMM8 && pc[1] == op_SUB_IMMX_X+0xc0+r_SP )
  373.             pc += 3;
  374.        }
  375.     if ( *pc == op_PUSH_REG+r_SI )
  376.        {    pc++;
  377.         if ( *pc == op_PUSH_REG+r_DI ) pc++;
  378.        }
  379.     if ( *pc == op_CMP_MR && pc[1] == (r_SP<<3)+6 && *(char _ds **)(pc + 2) == (char _ds *)&_stklen )
  380.         pc += 4;
  381.     len = pc - pcode;
  382.     if ( len < sizeof(trace_entry_code) )
  383.        {    /* Not enough room for entry code */
  384.         free((char *)rec);
  385.         return -1;
  386.        }
  387.     /* There is, unfortunately, no far version of memcpy. */
  388.     movedata(FP_SEG(pcode), FP_OFF(pcode), FP_SEG(pt), FP_OFF(pt), len);
  389.     pt += len;
  390.     *pt++ = op_JMP_FAR;
  391.     *(byte **)pt = pc;
  392.     rec->next = trace_list;
  393.     rec->name = name;
  394.     rec->arg_format = arg_format;
  395.     rec->retsize = retsize;
  396.     rec->entry_code.rec_seg = FP_SEG(rec);
  397.     rec->entry_code.rec_off = FP_OFF(rec);
  398.     rec->entry_code.print_args = (i_ptr)trace_print_arguments;
  399.     rec->exit_code.rec_seg = FP_SEG(rec);
  400.     rec->exit_code.rec_off = FP_OFF(rec);
  401.     rec->exit_code.print_result = (i_ptr)trace_print_result;
  402.     rec->count = 0;
  403.     trace_list = rec;
  404.     /* Patch the procedure entry */
  405.     *ptrace = trace_entry_template;
  406.     ptrace->entry = (i_ptr)&rec->entry_code;
  407.     return 0;
  408. }
  409.  
  410. /* Acquire the file to be used for tracing output. */
  411. static FILE *
  412. outfile()
  413. {    return (trace_output_file == NULL ? stdout : trace_output_file);
  414. }
  415.  
  416. /* Flush the output file if appropriate. */
  417. static void
  418. flushout()
  419. {    if ( trace_output_file == NULL || trace_output_file == stdout )
  420.         fflush(stdout);
  421. }
  422.  
  423. /* The following routine gets called at the entry */
  424. /* to the traced procedure.  It prints the arguments and returns. */
  425. bp_ret *
  426. trace_print_arguments(rec, sp)
  427.     trace_rec *rec;
  428.     bp_ret _ss *sp;
  429. {    bp_ret saved_bp_ret = *sp;
  430.     byte *retcode = saved_bp_ret.ret;
  431.     byte *morecode = (byte *)&rec->exit_code.PUSH_BP;
  432.     char _ss *args = (char _ss *)(sp + 1);
  433.     int argsize = 0;
  434.     bp_ret _ss *above;
  435.     FILE *f = outfile();
  436.     rec->count++;
  437.     /* Get the size of the arguments by looking at */
  438.     /* the instructions after the return */
  439.     if ( retcode[0] == op_ADD_IMM8 && retcode[1] == op_ADD_IMMX_X+0xc0+r_SP )
  440.         argsize = retcode[2];
  441.     else if ( retcode[0] == op_ADD_IMM && retcode[1] == op_ADD_IMMX_X+0xc0+r_SP )
  442.         argsize = *(int *)(retcode + 2);
  443.     else if ( retcode[0] == op_INC_REG+r_SP && retcode[1] == op_INC_REG+r_SP )
  444.         argsize = 2;
  445.     else if ( retcode[0] == op_POP_REG+r_CX )
  446.         argsize = (retcode[1] == op_POP_REG+r_CX ? 4 : 2);
  447.     else if ( retcode[0] == op_MOV_MR && retcode[1] == 0xc0+(r_SP<<3)+r_BP )
  448.         argsize = (char _ss *)saved_bp_ret.bp - args;
  449.     else
  450.         printf("Unknown calling sequence at %x:%x, abort\n",
  451.                FP_SEG(retcode), FP_OFF(retcode)),
  452.         exit(1);
  453.     fprintf(f, "%s called from %x:%x with ",
  454.         rec->name, FP_SEG(retcode), FP_OFF(retcode));
  455.     if ( rec->arg_format == NULL )
  456.         fprint_block((int *)args, argsize >> 1, f);
  457.     else
  458.         vfprintf(f, rec->arg_format, (va_list)args);
  459.     fputc('\n', f);
  460.     flushout();
  461.     /* Push the arguments down, saving the return and BP above them. */
  462.     /* Note that this smashes the arguments of trace_print_arguments. */
  463.     memcpy((char *)(args - sizeof(bp_ret)), (char *)args, argsize);
  464.     args -= sizeof(bp_ret);
  465.     above = (bp_ret _ss *)(args + argsize);
  466.     *above = saved_bp_ret;
  467.     ((byte **)args)[-1] = morecode;
  468.     return above;
  469. }
  470.  
  471. /* Print and return the result */
  472. long
  473. trace_print_result(rec, result, sp, bp)
  474.     trace_rec *rec;
  475.     long result;
  476.     int _ss *sp;
  477.     bp_ret _ss *bp;
  478. {    bp_ret temp_bp_ret;
  479.     FILE *f = outfile();
  480.     fprintf(f, "%s returns", rec->name);
  481.     switch ( rec->retsize )
  482.        {
  483.     case 0: break;
  484.     case 1: fprintf(f, " %x", (unsigned char)result); break;
  485.     case 2: fprintf(f, " %x", (unsigned short)result); break;
  486.     case 3:                /* ??? */
  487.     case 4: fprintf(f, " %lx", result); break;
  488.     default:
  489.        {    /* We are returning a structure */
  490.         fputs(" (struct)", f);
  491.         fprint_block((int *)result, rec->retsize >> 1, f);
  492.        }
  493.        }
  494.     fputc('\n', f);
  495.     flushout();
  496.     /* Move the saved BP and return back down below the args. */
  497.     /* We can smash the args at this point without concern. */
  498.     /* However, since the source and destination may overlap, */
  499.     /* and Turbo C isn't smart enough to handle this case, */
  500.     /* we have to use an intermediate variable. */
  501.     temp_bp_ret = *bp;
  502.     *(bp_ret *)(sp + 1) = temp_bp_ret;
  503.     return result;
  504. }
  505.  
  506. /* Procedure to print a block of words */
  507. static void
  508. fprint_block(ptr, count, f)
  509.     int *ptr;
  510.     int count;
  511.     FILE *f;
  512. {    int i;
  513.     for ( i = 0; i < count; i++ )
  514.         fprintf(f, "%5x", ptr[i]);
  515. }
  516.